﻿using System;
using System.Collections.Generic;
using System.Text;
using System.Diagnostics;
using System.Xml;
using System.Threading;
using System.Text.RegularExpressions;
using BizUnit;

namespace Acme.BizTalk.BizUnit.Tests.TestSteps
{    
    /// <summary>
    /// Replaces the EventLogCheckStep
    /// </summary>
    public class EventLogCheckStep : ITestStep
    {        
        #region ITestStep Members
        /// <summary>
        /// Replaces the EventLogCheckStep
        /// </summary>
        /// <param name="testConfig"></param>
        /// <param name="context"></param>
        public void Execute(System.Xml.XmlNode testConfig, Context context)
        {
            EventLogCheckArgs args = new EventLogCheckArgs(testConfig, context);

            Thread.Sleep(new TimeSpan(0, 0, args.DelayBeforeCheck));

            bool entryFound = false;
            DateTime stopChecking = DateTime.Now.AddSeconds(args.Timeout);            
            while ((stopChecking >= DateTime.Now))
            {
                entryFound = CheckForEvent(args, context);
                if (entryFound)
                    break;
            }

            if (!entryFound) //One last check once timeout reached
                entryFound = CheckForEvent(args, context);

            if (args.FailIfFound && entryFound)
                throw new ApplicationException("Found event log entry which should not be present.");

            if (args.FailIfFound == false && entryFound == false)
                throw new ApplicationException("Failed to find expected event log entry.");
        }
        #endregion

        /// <summary>
        /// Checks the event log for events matching the given criteria
        /// </summary>
        /// <param name="args"></param>
        /// <returns></returns>
        private static bool CheckForEvent(EventLogCheckArgs args, Context context)
        {
            using (EventLog log = new EventLog(args.LogName, args.MachineName))
            {
                EventLogEntryCollection entries = log.Entries;
                context.LogInfo("Scanning {0} event log entries from log '{1}' on machine '{2}'.", new object[] { entries.Count, args.LogName, args.MachineName });

                foreach (EventLogEntry entry in entries)
                {
                    //The entry doesnt include milliseconds so i will remvoe 1 second from each entry 
                    //to ensure we dont miss ones which could be applicable                    
                    if (entry.TimeGenerated >= args.StartDateTime.AddSeconds(-1))
                    {
                        if (entry.Source == args.Source
                            && entry.EntryType == args.LogEntryType
                            && (entry.InstanceId == args.EventId || args.EventId == 0))
                        {
                            if (args.List == null || args.List.Count == 0)
                            {                                
                                context.LogInfo("Successfully matched event log entry generated at '{0}'.", new object[] { entry.TimeGenerated });
                                context.LogData("Event log entry.", entry.Message);
                                return true;
                            }
                            else
                            {
                                foreach (XmlNode node in args.List)
                                {
                                    string innerText = node.InnerText;
                                    if (Regex.Match(entry.Message, innerText).Success)
                                    {                                        
                                        context.LogInfo("Successfully matched event log entry generated at '{0}'.", new object[] { entry.TimeGenerated });
                                        context.LogData("Event log entry.", entry.Message);
                                        return true;
                                    }                                    
                                }
                            }
                        }
                    }
                }                                
                Thread.Sleep(new TimeSpan(0, 0, args.SecondsBetweenChecks));                
            }
            return false;
        }
        /// <summary>
        /// Args for use in the event log check step
        /// </summary>
        private class EventLogCheckArgs
        {
            private int _Timeout;
            private int _SecondsBetweenChecks;
            private int _DelayBeforeCheck;
            private bool _FailIfFound;
            private string _MachineName;
            private int _EventId;
            private string _LogName;
            private string _Source;
            private string _Type;
            private EventLogEntryType _LogEntryType;
            private XmlNodeList _List;
            private DateTime _StartDateTime;

            /// <summary>
            /// Ctor
            /// </summary>
            /// <param name="testConfig"></param>
            /// <param name="context"></param>
            public EventLogCheckArgs(System.Xml.XmlNode testConfig, Context context)
            {
                this._Timeout = context.ReadConfigAsInt32(testConfig, "Timeout", true);
                this._SecondsBetweenChecks = context.ReadConfigAsInt32(testConfig, "IntervalBetweenChecks", true);
                this._DelayBeforeCheck = context.ReadConfigAsInt32(testConfig, "DelayBeforeCheck", true);
                this._FailIfFound = context.ReadConfigAsBool(testConfig, "FailIfFound", true);
                this._MachineName = context.ReadConfigAsString(testConfig, "Machine", true);
                this._EventId = context.ReadConfigAsInt32(testConfig, "EventId", true);
                this._LogName = context.ReadConfigAsString(testConfig, "EventLog");
                this._Source = context.ReadConfigAsString(testConfig, "Source");
                this._Type = context.ReadConfigAsString(testConfig, "Type");
                this._List = testConfig.SelectNodes("ValidationRegex");
                this._LogEntryType = (EventLogEntryType)Enum.Parse(typeof(EventLogEntryType), this._Type, true);
                this._StartDateTime = context.TestCaseStart;

                if (string.IsNullOrEmpty(this._MachineName))
                    this._MachineName = Environment.MachineName;
            }

            /// </remarks>
            public string LogName
            {
                get { return _LogName; }
                set { _LogName = value; }
            }
            /// </remarks>
            public string Source
            {
                get { return _Source; }
                set { _Source = value; }
            }
            /// </remarks>
            public string Type
            {
                get { return _Type; }
                set { _Type = value; }
            }
            /// </remarks>
            public EventLogEntryType LogEntryType
            {
                get { return _LogEntryType; }
                set { _LogEntryType = value; }
            }
            /// </remarks>
            public XmlNodeList List
            {
                get { return _List; }
                set { _List = value; }
            }
            /// </remarks>
            public DateTime StartDateTime
            {
                get { return _StartDateTime; }
                set { _StartDateTime = value; }
            }
            /// </remarks>
            public int EventId
            {
                get { return _EventId; }
                set { _EventId = value; }
            }
            /// </remarks>
            public string MachineName
            {
                get { return _MachineName; }
                set { _MachineName = value; }
            }
            /// </remarks>
            public bool FailIfFound
            {
                get { return _FailIfFound; }
                set { _FailIfFound = value; }
            }
            /// </remarks>
            public int DelayBeforeCheck
            {
                get { return _DelayBeforeCheck; }
                set { _DelayBeforeCheck = value; }
            }
            /// </remarks>
            public int SecondsBetweenChecks
            {
                get { return _SecondsBetweenChecks; }
                set { _SecondsBetweenChecks = value; }
            }
            /// </remarks>
            public int Timeout
            {
                get { return _Timeout; }
                set { _Timeout = value; }
            }
        }
    }
}


